#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<queue>
#include<ctime>
#include<cstdlib>

using namespace std;

const int N = 5;

template<class T>

class BlockQueue
{
public:
  BlockQueue(int num = N)
    :_capacity(num)
  {
    pthread_mutex_init(&_mutex,nullptr);
    pthread_cond_init(&_consumer_cond,nullptr);
    pthread_cond_init(&_productor_cond,nullptr);
  }

  void push(const T& key)
  {
    pthread_mutex_lock(&_mutex);
    if(IsFull())
    {
      pthread_cond_wait(&_productor_cond,&_mutex);
    }
    _data.push(key);
    pthread_cond_signal(&_consumer_cond);
    pthread_mutex_unlock(&_mutex);
  }

  void pop(T& key)
  {
    pthread_mutex_lock(&_mutex);
    if(IsEmpty())
    {
      pthread_cond_wait(&_consumer_cond,&_mutex);
    }
    key = _data.front();
    _data.pop();
    pthread_cond_signal(&_productor_cond);
    pthread_mutex_unlock(&_mutex);
  }

  bool IsEmpty() {return _data.empty();}

  bool IsFull() {return _data.size() == _capacity;}

  ~BlockQueue()
  {
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_consumer_cond);
    pthread_cond_destroy(&_productor_cond);
  }
private:
  queue<T> _data;
  int _capacity;
  pthread_mutex_t _mutex;
  pthread_cond_t _consumer_cond;
  pthread_cond_t _productor_cond;
};

